﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Windows.Media;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Utilities;
using Microsoft.VisualStudio.Language.StandardClassification;
using BooPegLexer;

namespace BooSyntax
{

    #region Provider definition
    /// <summary>
    /// This class causes a classifier to be added to the set of classifiers. Since 
    /// the content type is set to "text", this classifier applies to all text files
    /// </summary>
    [Export(typeof(IClassifierProvider))]
    [ContentType("text")]
    internal class BooSyntaxProvider : IClassifierProvider
    {
        /// <summary>
        /// Import the classification registry to be used for getting a reference
        /// to the custom classification type later.
        /// </summary>
        [Import]
        internal IClassificationTypeRegistryService ClassificationRegistry = null; // Set via MEF

        public IClassifier GetClassifier(ITextBuffer buffer)
        {
            return buffer.Properties.GetOrCreateSingletonProperty<BooSyntax>(delegate { return new BooSyntax(ClassificationRegistry, buffer); });
        }
    }
    #endregion //provider def

    #region Classifier
    /// <summary>
    /// Classifier that classifies all text as an instance of the OrinaryClassifierType
    /// </summary>
    class BooSyntax : IClassifier
    {
        private BooPegLexer.ILexer _lexer2;
        private BooPegLexer.PegToken token = new BooPegLexer.PegToken();  

        private IClassificationType _keywordType;
        private IClassificationType _whiteSpaceType;
        private IClassificationType _numberType;
        private IClassificationType _commentType;
        private IClassificationType _identifierType;
        private IClassificationType _stringType;
        private IClassificationType _operatorType;


        internal BooSyntax(IClassificationTypeRegistryService registry, ITextBuffer buffer)
        {
          
            _lexer2 = new BooPegLexer.PegLexer();
            _lexer2.Initialize(new string[] { }, new string[] { });

            _whiteSpaceType = registry.GetClassificationType(PredefinedClassificationTypeNames.WhiteSpace);
            _keywordType = registry.GetClassificationType(PredefinedClassificationTypeNames.Keyword);
            _numberType = registry.GetClassificationType(PredefinedClassificationTypeNames.Number);
            _commentType = registry.GetClassificationType(PredefinedClassificationTypeNames.Comment);
            _identifierType = registry.GetClassificationType(PredefinedClassificationTypeNames.Identifier);
            _stringType = registry.GetClassificationType(PredefinedClassificationTypeNames.String);
            _operatorType = registry.GetClassificationType(PredefinedClassificationTypeNames.Operator);
        }


        /// <summary>
        /// This method scans the given SnapshotSpan for potential matches for this classification.
        /// In this instance, it classifies everything and returns each span as a new ClassificationSpan.
        /// </summary>
        /// <param name="trackingSpan">The span currently being classified</param>
        /// <returns>A list of ClassificationSpans that represent spans identified to be of this classification</returns>
        public IList<ClassificationSpan> GetClassificationSpans(SnapshotSpan span)
        {
            /* TODO! Multiline comments */

            //create a list to hold the results
            List<ClassificationSpan> classifications = new List<ClassificationSpan>();

            int state = 0;
            _lexer2.SetSource(span.GetText());

            while (_lexer2.NextToken(token, ref state))
            {
                IClassificationType classification_type = _whiteSpaceType;

                if (token.Type == PegTokenType.EOL)
                    break;

                switch (token.Type)
                {
                    case PegTokenType.Keyword:
                        classification_type = _keywordType;
                        break;

                    case PegTokenType.DoubleWhackLineComment:
                    case PegTokenType.NumberSignLineComment:
                    case PegTokenType.MlComment:
                    case PegTokenType.MlCommentOpen:
                    case PegTokenType.MlCommentClose:
                        classification_type = _commentType;
                        break;

                    case PegTokenType.Identifier:
                        classification_type = _identifierType;
                        break;

                    case PegTokenType.SingleQuoteString:
                    case PegTokenType.DoubleQuoteString:
                    case PegTokenType.TripleQuoteString:
                    case PegTokenType.TripleQuoteStringClose:
                    case PegTokenType.TripleQuoteStringOpen:
                        classification_type = _stringType;
                        break;

                    case PegTokenType.FloatLiteral:
                    case PegTokenType.IntegerLiteral:
                        classification_type = _numberType;
                        break;

                    case PegTokenType.AdditionSign:
                    case PegTokenType.DivisionSign:
                    case PegTokenType.EqualsSign:
                    case PegTokenType.MultiplicationSign:
                    case PegTokenType.SubtractionSign:
                        classification_type = _operatorType;
                        break;

                    case PegTokenType.Whitespace:
                        classification_type = _whiteSpaceType;
                        break;
                }

                Span new_span = new Span(span.Start.Position+ token.StartIndex, token.EndIndex - token.StartIndex + 1);

                classifications.Add(new ClassificationSpan(new SnapshotSpan(span.Snapshot , new_span), classification_type));
            }


            return classifications;
        }

#pragma warning disable 67
        // This event gets raised if a non-text change would affect the classification in some way,
        // for example typing /* would cause the classification to change in C# without directly
        // affecting the span.
        public event EventHandler<ClassificationChangedEventArgs> ClassificationChanged;
#pragma warning restore 67
    }
    #endregion //Classifier
}
